/********************************************************************
	
	Tau Virtual Machine
	
	integer.c
	
	Copyright (c) 2009 Tau Virtual Machine Project
	
	See COPYING.txt for copying and redistribution conditions.
	
	Author:  Matt C.
	Date:  Aug 05, 2009
	
********************************************************************/

#include "tau_types.h"

// ROUTINES *********************************************************

TauValue* TauMakeInteger (int i)
{
	TauValue* v;
	
	v = (TauValue*) malloc(sizeof(TauValue));
	v->type = T_INTEGER;
	v->refs = 0;
	v->data.i = i;
	
	return v;
}

// Negate ***********************************************************

TauValue* TauIntegerNegate (TauValue* vs)
{
	// - I -> I
	
	return TauMakeInteger(-vs->data.i);
}

// Add **************************************************************

TauValue* TauIntegerAdd (TauValue* vs, TauValue* vt)
{
	TauValue* vd;
	double cr, ci;
	double r;
	int i;
	
	// I + B -> I
	// I + I -> I
	// I + R -> R
	// I + C -> C
	
	switch (vt->type)
	{
		case T_INTEGER:
			i = vs->data.i + vt->data.i;
			vd = TauMakeInteger(i);
			break;
		
		case T_BOOLEAN:
			i = vs->data.i + vt->data.b;
			vd = TauMakeInteger(i);
			break;
		
		case T_REAL:
			r = (double)(vs->data.i) + vt->data.r;
			vd = TauMakeReal(r);
			break;
		
		case T_COMPLEX:
			cr = (double)(vs->data.i) + vt->data.c->real;
			ci = vt->data.c->imag;
			vd = TauMakeComplex(cr, ci);
			break;
		
		default:
			// exception
			break;
	}

	return vd;
}

// Subtract *********************************************************

TauValue* TauIntegerSubtract (TauValue* vs, TauValue* vt)
{
	TauValue* vd;
	double cr, ci;
	double r;
	int i;
	
	// I - B -> I
	// I - I -> I
	// I - R -> R
	// I - C -> C
	
	switch (vt->type)
	{
		case T_INTEGER:
			i = vs->data.i - vt->data.i;
			vd = TauMakeInteger(i);
			break;
		
		case T_BOOLEAN:
			i = vs->data.i - vt->data.b;
			vd = TauMakeInteger(i);
			break;
		
		case T_REAL:
			r = (double)(vs->data.i) - vt->data.r;
			vd = TauMakeReal(r);
			break;
		
		case T_COMPLEX:
			cr = (double)(vs->data.i) - vt->data.c->real;
			ci = -vt->data.c->imag;
			vd = TauMakeComplex(cr, ci);
			break;

		default:
			// exception
			break;
	}

	return vd;
}

// Multiply *********************************************************

TauValue* TauIntegerMultiply (TauValue* vs, TauValue* vt)
{
	TauValue* vd;
	double cr, ci;
	double r;
	int i;
	
	// I * B -> I
	// I * I -> I
	// I * R -> R
	// I * C -> C
	
	switch (vt->type)
	{
		case T_INTEGER:
			i = vs->data.i * vt->data.i;
			vd = TauMakeInteger(i);
			break;
		
		case T_BOOLEAN:
			i = vs->data.i * vt->data.b;
			vd = TauMakeInteger(i);
			break;

		case T_REAL:
			r = (double)(vs->data.i) * vt->data.r;
			vd = TauMakeReal(r);
			break;
		
		case T_COMPLEX:
			d = (double)(vs->data.i);
			cr = d * vt->data.c->real;
			ci = d * vt->data.c->imag;
			vd = TauMakeComplex(cr, ci);
			break;

		default:
			// exception
			break;
	}

	return vd;
}

// Divide ***********************************************************

TauValue* TauIntegerDivide (TauValue* vs, TauValue* vt)
{
	TauValue* vd;
	double cr, ci;
	double r;
	int i;
	
	double numr, numi, den;
	double x, y;
	
	// I / B -> R
	// I / I -> R
	// I / R -> R
	// I / C -> C
	
	switch (vt->type)
	{
		case T_INTEGER:
			if (vt->data.i == 0)
				; // exception - divide by zero
			else
			{
				i = (double)(vs->data.i) / vt->data.i;
				vd = TauMakeInteger(i);
			}
			break;
		
		case T_BOOLEAN:
			if (!vt->data.b)
				; // exception - divide by zero
			else
			{
				i = (double)(vs->data.i) / vt->data.b;
				vd = TauMakeInteger(i);
			}
			break;

		case T_REAL:
			if (vt->data.r == 0.0)
				; // exception - divide by zero
			else
			{
				d = (double)(vs->data.i) / vt->data.r;
				vd = TauMakeReal(r);
			}
			break;
		
		case T_COMPLEX:
			d = (double)(vs->data.i);
			
			// vt format: x + jy
			x = vt->data.c->real;
			y = vt->data.c->imag;
			den = x*x + y*y;
			numr = d * x;
			numi = d * -y;
			cr = numr/den;
			ci = numi/den;
			
			cr = d * vt->data.c->real;
			ci = d * vt->data.c->imag;
			vd = TauMakeComplex(cr, ci);
			break;

		default:
			// exception
			break;
	}

	return vd;
}

// Modulo ***********************************************************

TauValue* TauIntegerModulo (TauValue* vs, TauValue* vt)
{
	TauValue* vd;
	int i;
	
	// I % B -> I
	// I % I -> I
	// I % R -> X
	// I % C -> X
	
	switch (vt->type)
	{
		case T_INTEGER:
			i = vs->data.i % vt->data.i;
			vd = TauMakeInteger(i);
			break;
		
		case T_BOOLEAN:
			i = vs->data.i % vt->data.b;
			vd = TauMakeInteger(i);
			break;
		
		case T_REAL:
			// exception - undefined
			break;
		
		case T_COMPLEX:
			// exception - undefined
			break;
		
		default:
			// exception
			break;
	}

	return vd;
}

// FloorDivide ******************************************************

TauValue* TauIntegerFloorDivide (TauValue* vs, TauValue* vt)
{
	TauValue* vd;
	double cr, ci;
	double r;
	int i;
	
	// I // B -> I
	// I // I -> I
	// I // R -> R
	// I // C -> X
	
	switch (vt->type)
	{
		case T_INTEGER:
			if (vt->data.i == 0)
				; // exception - divide by zero
			else
			{
				i = vs->data.i / vt->data.i;
				vd = TauMakeInteger(i);
			}
			break;
		
		case T_BOOLEAN:
			if (!vt->data.b)
				; // exception - divide by zero
			else
			{
				i = vs->data.i / vt->data.b;
				vd = TauMakeInteger(i);
			}
			break;
		
		case T_REAL:
			// revert to normal division
			if (vt->data.r == 0.0)
				; // exception - divide by zero
			else
			{
				d = (double)(vs->data.i) / vt->data.r;
				vd = TauMakeReal(r);
			}
			break;
		
		case T_COMPLEX:
			// exception - undefined
			break;
		
		default:
			// exception
			break;
	}
	
	return vd;
}

// BitNot ***********************************************************

TauValue* TauIntegerBitNot (TauValue* vs)
{
	TauValue* vd;
	
	// ~ I -> I
	
	vd = TauMakeInteger(~vs->data.i);

	return vd;
}

// And **************************************************************

TauValue* TauIntegerAnd (TauValue* vs, TauValue* vt)
{
	TauValue* vd;
	int i;
	
	// I & B -> I
	// I & I -> I
	// I & R -> X
	// I & C -> X
	
	switch (vt->type)
	{
		case T_INTEGER:
			i = vs->data.i & vt->data.i;
			vd = TauMakeInteger(i);
			break;
		
		case T_BOOLEAN:
			i = vs->data.i & vt->data.b;
			vd = TauMakeInteger(i);
			break;
		
		case T_REAL:
			// exception - undefined
			break;
		
		case T_COMPLEX:
			// exception - undefined
			break;
		
		default:
			// exception
			break;
	}
	
	return vd;
}

// Or ***************************************************************

TauValue* TauIntegerOr (TauValue* vs, TauValue* vt)
{
	TauValue* vd;
	int i;
	
	// I | B -> I
	// I | I -> I
	// I | R -> X
	// I | C -> X
	
	switch (vt->type)
	{
		case T_INTEGER:
			i = vs->data.i | vt->data.i;
			vd = TauMakeInteger(i);
			break;
		
		case T_BOOLEAN:
			i = vs->data.i | vt->data.b;
			vd = TauMakeInteger(i);
			break;
		
		case T_REAL:
			// exception - undefined
			break;
		
		case T_COMPLEX:
			// exception - undefined
			break;
		
		default:
			// exception
			break;
	}
	
	return vd;
}

// Xor **************************************************************

TauValue* TauIntegerXor (TauValue* vs, TauValue* vt)
{
	TauValue* vd;
	int i;
	
	// I ^ B -> I
	// I ^ I -> I
	// I ^ R -> X
	// I ^ C -> X
	
	switch (vt->type)
	{
		case T_INTEGER:
			i = vs->data.i ^ vt->data.i;
			vd = TauMakeInteger(i);
			break;
		
		case T_BOOLEAN:
			i = vs->data.i ^ vt->data.b;
			vd = TauMakeInteger(i);
			break;
		
		case T_REAL:
			// exception - undefined
			break;
		
		case T_COMPLEX:
			// exception - undefined
			break;
		
		default:
			// exception
			break;
	}
	
	return vd;
}

// Not **************************************************************

TauValue* TauIntegerNot (TauValue* vs)
{
	TauValue* vd;
	
	// ! I -> I
	
	vd = TauMakeInteger(!vs->data.i);
	
	return vd;
}

// Equal ************************************************************

TauValue* TauIntegerEqual (TauValue* vs, TauValue* vt)
{
	TauValue* vd;
	int cond1, cond2;
	int b;
	
	// I == B -> B
	// I == I -> B
	// I == R -> B
	// I == C -> B
	
	switch (vt->type)
	{
		case T_INTEGER:
			b = (vs->data.i == vt->data.i);
			vd = TauMakeBoolean(b);
			break;
		
		case T_BOOLEAN:
			b = (vs->data.i == vt->data.b);
			vd = TauMakeBoolean(b);
			break;
		
		case T_REAL:
			b = ((double)(vs->data.i) == vt->data.r);
			vd = TauMakeBoolean(b);
			break;
		
		case T_COMPLEX:
			cond1 = ((double)(vs->data.i) == vt->data.c->real);
			cond2 = (vt->data.c->imag == 0.0);
			b = (cond1 && cond2);
			vd = TauMakeBoolean(b);
			break;
		
		default:
			// exception
			break;
	}
	
	return vd;
}

// NotEqual *********************************************************

TauValue* TauIntegerNotEqual (TauValue* vs, TauValue* vt)
{
	TauValue* vd;
	int cond1, cond2;
	int b;
	
	// I != B -> B
	// I != I -> B
	// I != R -> B
	// I != C -> B
	
	switch (vt->type)
	{
		case T_INTEGER:
			b = (vs->data.i != vt->data.i);
			vd = TauMakeBoolean(b);
			break;
		
		case T_BOOLEAN:
			b = (vs->data.i != vt->data.b);
			vd = TauMakeBoolean(b);
			break;
		
		case T_REAL:
			b = ((double)(vs->data.i) != vt->data.r);
			vd = TauMakeBoolean(b);
			break;
		
		case T_COMPLEX:
			cond1 = ((double)(vs->data.i) != vt->data.c->real);
			cond2 = (vt->data.c->imag != 0.0);
			b = (cond1 || cond2);
			vd = TauMakeBoolean(b);
			break;
		
		default:
			// exception
			break;
	}
	
	return vd;
}

// Greater **********************************************************

TauValue* TauIntegerGreater (TauValue* vs, TauValue* vt)
{
	TauValue* vd;
	int b;
	
	// I > B -> B
	// I > I -> B
	// I > R -> B
	// I > C -> X
	
	switch (vt->type)
	{
		case T_INTEGER:
			b = (vs->data.i > vt->data.i);
			vd = TauMakeBoolean(b);
			break;
		
		case T_BOOLEAN:
			b = (vs->data.i > vt->data.b);
			vd = TauMakeBoolean(b);
			break;
		
		case T_REAL:
			b = ((double)(vs->data.i) > vt->data.r);
			vd = TauMakeBoolean(b);
			break;
		
		case T_COMPLEX:
			// exception - undefined
			break;
		
		default:
			// exception
			break;
	}
	
	return vd;
}

// Less *************************************************************

TauValue* TauIntegerLess (TauValue* vs, TauValue* vt)
{
	TauValue* vd;
	int b;
	
	// I < B -> B
	// I < I -> B
	// I < R -> B
	// I < C -> X
	
	switch (vt->type)
	{
		case T_INTEGER:
			b = (vs->data.i < vt->data.i);
			vd = TauMakeBoolean(b);
			break;
		
		case T_BOOLEAN:
			b = (vs->data.i < vt->data.b);
			vd = TauMakeBoolean(b);
			break;
		
		case T_REAL:
			b = ((double)(vs->data.i) < vt->data.r);
			vd = TauMakeBoolean(b);
			break;
		
		case T_COMPLEX:
			// exception - undefined
			break;
		
		default:
			// exception
			break;
	}
	
	return vd;
}

// GreaterEqual *****************************************************

TauValue* TauIntegerGreaterEqual (TauValue* vs, TauValue* vt)
{
	TauValue* vd;
	int b;
	
	// I >= B -> B
	// I >= I -> B
	// I >= R -> B
	// I >= C -> X
	
	switch (vt->type)
	{
		case T_INTEGER:
			b = (vs->data.i >= vt->data.i);
			vd = TauMakeBoolean(b);
			break;
		
		case T_BOOLEAN:
			b = (vs->data.i >= vt->data.b);
			vd = TauMakeBoolean(b);
			break;
		
		case T_REAL:
			b = ((double)(vs->data.i) >= vt->data.r);
			vd = TauMakeBoolean(b);
			break;
		
		case T_COMPLEX:
			// exception - undefined
			break;
		
		default:
			// exception
			break;
	}
	
	return vd;
}
// LessEqual ********************************************************

TauValue* TauIntegerLessEqual (TauValue* vs, TauValue* vt)
{
	TauValue* vd;
	int b;
	
	// I <= B -> B
	// I <= I -> B
	// I <= R -> B
	// I <= C -> X
	
	switch (vt->type)
	{
		case T_INTEGER:
			b = (vs->data.i <= vt->data.i);
			vd = TauMakeBoolean(b);
			break;
		
		case T_BOOLEAN:
			b = (vs->data.i <= vt->data.b);
			vd = TauMakeBoolean(b);
			break;
		
		case T_REAL:
			b = ((double)(vs->data.i) <= vt->data.r);
			vd = TauMakeBoolean(b);
			break;
		
		case T_COMPLEX:
			// exception - undefined
			break;
		
		default:
			// exception
			break;
	}
	
	return vd;
}

// END **************************************************************



